package net.qiqbframework.spring.accessing;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.This;
import net.bytebuddy.matcher.ElementMatchers;
import net.qiqbframework.accessing.ClientApi;

public class ProxyFactory {
    public static <T> T createProxy(Class<T> interfaceClass, T targetInstance) throws Exception {
        if (!interfaceClass.isAnnotationPresent(ClientApi.class)) {
            throw new IllegalArgumentException("The provided class is not annotated with @ProxyInterface");
        }

        Class<? extends T> proxyClass = new ByteBuddy()
                .subclass(interfaceClass)
                .method(ElementMatchers.any())
                .intercept(MethodDelegation.to(new MethodInterceptor(targetInstance)))
                .make()
                .load(interfaceClass.getClassLoader())
                .getLoaded();

        return proxyClass.getDeclaredConstructor().newInstance();
    }

    public static class MethodInterceptor {
        private final Object targetInstance;

        public MethodInterceptor(Object targetInstance) {
            this.targetInstance = targetInstance;
        }

        @RuntimeType
        public Object intercept(@This Object proxy, @AllArguments Object[] args) throws Exception {
            return proxy.getClass()
                    .getSuperclass()
                    .getMethod(Thread.currentThread().getStackTrace()[2].getMethodName())
                    .invoke(targetInstance, args);
        }
    }
}